package com.quectel.business.common.parking;

import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.dms.AccessorUtils;
import com.quectel.business.dms.application.access.accessor.ipg.AbstractIntelligentParkingGateAccessor;
import com.quectel.business.dms.application.access.accessor.ipg.model.AuthCarCmd;
import com.quectel.business.dms.application.access.accessor.ipg.model.GuardOpenResp;
import com.quectel.constant.core.device.DeviceCategoryCodeConstants;
import com.quectel.constant.core.device.DeviceConstants;
import com.quectel.constant.core.parking.ParkingOrderConstants;
import com.quectel.constant.core.parkingcard.ParkingCardMemberDeviceConstants;
import com.quectel.constant.core.parkingcard.ParkingCardOrderDeviceConstants;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.dto.DeviceTypeDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.DeviceTypeService;
import com.quectel.core.module.parking.dto.ParkingOrderDto;
import com.quectel.core.module.parking.service.ParkingOrderService;
import com.quectel.core.module.parkingcard.dto.ParkingCardMemberDeviceDto;
import com.quectel.core.module.parkingcard.dto.ParkingCardMemberDto;
import com.quectel.core.module.parkingcard.dto.ParkingCardOrderDeviceDto;
import com.quectel.core.module.parkingcard.service.ParkingCardMemberDeviceService;
import com.quectel.core.module.parkingcard.service.ParkingCardMemberService;
import com.quectel.core.module.parkingcard.service.ParkingCardOrderDeviceService;
import com.quectel.core.module.parkingcard.service.ParkingCardOrderService;
import com.quectel.util.thread.ThreadPoolUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Component
@Slf4j
public class ParkingGateCmdBusiness implements DisposableBean {
    private static final ThreadPoolExecutor CMD_EXEC_POOL = ThreadPoolUtils.createCommonIOBoundPool("parking-gate-cmd-pool");

    @Override
    public void destroy() throws Exception {
        ThreadPoolUtils.shutdown(CMD_EXEC_POOL);
    }

    @DubboReference
    private ParkingCardOrderService parkingCardOrderService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private DeviceTypeService deviceTypeService;
    @DubboReference
    private ParkingCardOrderDeviceService parkingCardOrderDeviceService;
    @DubboReference
    private ParkingCardMemberService parkingCardMemberService;
    @DubboReference
    private ParkingCardMemberDeviceService parkingCardMemberDeviceService;
    @DubboReference
    private ParkingOrderService parkingOrderService;

    /**
     * 取消授权
     *
     * @param villageId    小区id
     * @param license      车牌号
     * @param licenseColor 车牌颜色
     */
    public void revoke(Long villageId, String license, Byte licenseColor) {
        CMD_EXEC_POOL.submit(() -> {
            Map<String, Object> queryDeviceParams = new HashMap<>();
            queryDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getVillageId), villageId);
            queryDeviceParams.put(DeviceConstants.QueryParamEnum.DEVICE_CATEGORY_CODE.getParamKey(), DeviceCategoryCodeConstants.INTELLIGENT_PARKING_GATE);
            List<DeviceDto> deviceDtos = deviceService.queryList(queryDeviceParams);

            deviceDtos.stream().collect(Collectors.groupingBy(DeviceDto::getDeviceTypeId)).forEach((k, v) -> {
                DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(k);
                AbstractIntelligentParkingGateAccessor intelligentParkingGateAccessor = AccessorUtils.getIntelligentParkingGateAccessor(deviceTypeDto);
                Preconditions.checkNotNull(intelligentParkingGateAccessor);

                final boolean supportCheck = intelligentParkingGateAccessor.abilities().contains(AbstractIntelligentParkingGateAccessor.AbilityEnum.AUTH_CAR);

                if (supportCheck) {
                    // 道闸支持授权车辆
                    List<AuthCarCmd.DeviceInfo> deviceInfoList = v.parallelStream().map(o -> {
                        AuthCarCmd.DeviceInfo deviceInfo = new AuthCarCmd.DeviceInfo();
                        deviceInfo.setApplicationId(o.getApplicationId());
                        deviceInfo.setDeviceUniqueId(o.getUniqueId());
                        return deviceInfo;
                    }).collect(Collectors.toList());

                    intelligentParkingGateAccessor.authCar(AuthCarCmd.builder()
                            .type(AuthCarCmd.TypeEnum.DEL.getType())
                            .deviceInfoList(deviceInfoList)
                            .license(license)
                            .licenseColor(licenseColor)
                            .build());
                }

                ParkingCardMemberDto memberDto = parkingCardMemberService.selectOneMember(villageId, license, licenseColor);
                Preconditions.checkNotNull(memberDto);

                // 道闸不支持授权车辆，直接把不支持的关系保存
                List<ParkingCardMemberDeviceDto> memberDeviceRefDtos = new ArrayList<>();

                for (DeviceDto deviceDto : v) {
                    ParkingCardMemberDeviceDto parkingCardMemberDeviceDto = new ParkingCardMemberDeviceDto();
                    parkingCardMemberDeviceDto.setParkingCardMemberId(memberDto.getId());
                    parkingCardMemberDeviceDto.setDeviceId(deviceDto.getId());
                    parkingCardMemberDeviceDto.setStatus(supportCheck ? ParkingCardOrderDeviceConstants.StatusEnum.INIT.getStatus() : ParkingCardOrderDeviceConstants.StatusEnum.NOT_SUPPORTED.getStatus());
                    parkingCardMemberDeviceDto.setAuthType(ParkingCardMemberDeviceConstants.AuthTypeEnum.REVOKE.getAuthType());
                    memberDeviceRefDtos.add(parkingCardMemberDeviceDto);
                }

                parkingCardMemberDeviceService.cleanAndSaveBatch(memberDeviceRefDtos);
            });
        });
    }

    /**
     * 根据会员授权车辆
     *
     * @param memberId 停车会员id
     */
    public void authorizeByMember(Long memberId) {
        ParkingCardMemberDto memberDto = parkingCardMemberService.selectById(memberId);
        Preconditions.checkNotNull(memberDto);
        authorize(memberDto.getVillageId(), memberDto.getLicense(), memberDto.getLicenseColor(), memberDto.getDeadline(), null);
    }

    /**
     * 根据办卡订单授权车辆
     *
     * @param villageId    小区id
     * @param license      车牌号
     * @param licenseColor 车牌颜色
     * @param deadline     截止日
     * @param orderId      办卡业务订单id 可以为null
     */
    public void authorize(Long villageId, String license, Byte licenseColor, Date deadline, Long orderId) {
        CMD_EXEC_POOL.submit(() -> {
            Map<String, Object> queryDeviceParams = new HashMap<>();
            queryDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getVillageId), villageId);
            queryDeviceParams.put(DeviceConstants.QueryParamEnum.DEVICE_CATEGORY_CODE.getParamKey(), DeviceCategoryCodeConstants.INTELLIGENT_PARKING_GATE);
            List<DeviceDto> deviceDtos = deviceService.queryList(queryDeviceParams);

            deviceDtos.stream().collect(Collectors.groupingBy(DeviceDto::getDeviceTypeId)).forEach((k, v) -> {
                DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(k);
                AbstractIntelligentParkingGateAccessor intelligentParkingGateAccessor = AccessorUtils.getIntelligentParkingGateAccessor(deviceTypeDto);
                Preconditions.checkNotNull(intelligentParkingGateAccessor);

                final boolean supportCheck = intelligentParkingGateAccessor.abilities().contains(AbstractIntelligentParkingGateAccessor.AbilityEnum.AUTH_CAR);

                if (supportCheck) {
                    // 道闸支持授权车辆
                    Map<String, Object> payload = new HashMap<>();
                    if (orderId != null) {
                        payload.put(LambdaUtil.getFieldName(ParkingCardOrderDeviceDto::getParkingCardOrderId), orderId);
                    }
                    List<AuthCarCmd.DeviceInfo> deviceInfoList = v.parallelStream().map(o -> {
                        AuthCarCmd.DeviceInfo deviceInfo = new AuthCarCmd.DeviceInfo();
                        deviceInfo.setApplicationId(o.getApplicationId());
                        deviceInfo.setDeviceUniqueId(o.getUniqueId());
                        return deviceInfo;
                    }).collect(Collectors.toList());

                    intelligentParkingGateAccessor.authCar(AuthCarCmd.builder()
                            .type(AuthCarCmd.TypeEnum.ADD.getType())
                            .deviceInfoList(deviceInfoList)
                            .license(license)
                            .licenseColor(licenseColor)
                            .deadline(deadline)
                            .payload(payload)
                            .build());
                }

                ParkingCardMemberDto memberDto = parkingCardMemberService.selectOneMember(villageId, license, licenseColor);
                Preconditions.checkNotNull(memberDto);

                // 道闸不支持授权车辆，直接把不支持的关系保存
                List<ParkingCardOrderDeviceDto> orderDeviceRefDtos = new ArrayList<>();
                List<ParkingCardMemberDeviceDto> memberDeviceRefDtos = new ArrayList<>();

                for (DeviceDto deviceDto : v) {
                    if (orderId != null) {
                        ParkingCardOrderDeviceDto parkingCardOrderDeviceDto = new ParkingCardOrderDeviceDto();
                        parkingCardOrderDeviceDto.setParkingCardOrderId(orderId);
                        parkingCardOrderDeviceDto.setDeviceId(deviceDto.getId());
                        parkingCardOrderDeviceDto.setStatus(supportCheck ? ParkingCardOrderDeviceConstants.StatusEnum.INIT.getStatus() : ParkingCardOrderDeviceConstants.StatusEnum.NOT_SUPPORTED.getStatus());
                        orderDeviceRefDtos.add(parkingCardOrderDeviceDto);
                    }

                    ParkingCardMemberDeviceDto parkingCardMemberDeviceDto = new ParkingCardMemberDeviceDto();
                    parkingCardMemberDeviceDto.setParkingCardMemberId(memberDto.getId());
                    parkingCardMemberDeviceDto.setDeviceId(deviceDto.getId());
                    parkingCardMemberDeviceDto.setStatus(supportCheck ? ParkingCardOrderDeviceConstants.StatusEnum.INIT.getStatus() : ParkingCardOrderDeviceConstants.StatusEnum.NOT_SUPPORTED.getStatus());
                    parkingCardMemberDeviceDto.setAuthType(ParkingCardMemberDeviceConstants.AuthTypeEnum.AUTHORIZE.getAuthType());
                    memberDeviceRefDtos.add(parkingCardMemberDeviceDto);
                }

                parkingCardMemberDeviceService.cleanAndSaveBatch(memberDeviceRefDtos);
                if (CollectionUtils.isNotEmpty(orderDeviceRefDtos)) {
                    // orderId未传，没有order与devic的关系
                    parkingCardOrderDeviceService.saveBatch(orderDeviceRefDtos);
                }
            });
        });
    }

    /**
     * 开闸
     * @param crimpingDeviceId 压线设备id
     * @param orderId 订单id
     */
    public void open(Long crimpingDeviceId, Long orderId) {
        CMD_EXEC_POOL.submit(()->{
            DeviceDto deviceDto = deviceService.selectCacheById(crimpingDeviceId);
            if (Objects.nonNull(deviceDto)) {
                DeviceTypeDto deviceType = deviceDto.getDeviceType();
                AbstractIntelligentParkingGateAccessor intelligentParkingGateAccessor = AccessorUtils.getIntelligentParkingGateAccessor(deviceType);
                Preconditions.checkNotNull(intelligentParkingGateAccessor);

                GuardOpenResp resp = intelligentParkingGateAccessor.open(deviceDto.getApplicationId(), deviceDto.getUniqueId());
                if (resp.getSupportSync() && resp.getOpenResult()) {
                    // 支持同步返回结果，并且开闸成功
                    ParkingOrderDto newlyDto = new ParkingOrderDto();
                    newlyDto.setId(orderId);
                    newlyDto.setOpenGuardStatus(ParkingOrderConstants.GuardOpenStatusEnum.OPENED.getOpenStatus());
                    parkingOrderService.updateById(newlyDto);
                }
            }
        });
    }
}
